package com.stars.easyms.logger.support.logback.joran.spi;

import ch.qos.logback.core.Context;
import ch.qos.logback.core.joran.action.Action;
import ch.qos.logback.core.joran.spi.ElementPath;
import ch.qos.logback.core.joran.spi.ElementSelector;
import ch.qos.logback.core.joran.spi.RuleStore;
import ch.qos.logback.core.spi.ContextAwareBase;
import ch.qos.logback.core.util.OptionHelper;
import com.stars.easyms.logger.support.EasyMsOverride;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * <p>className: EasyMsSimpleRuleStore</p>
 * <p>description: 重写SimpleRuleStore类</p>
 *
 * @author guoguifang
 * @date 2019-08-20 11:41
 * @since 1.3.0
 */
public final class EasyMsSimpleRuleStore extends ContextAwareBase implements RuleStore, EasyMsOverride {

    private static final String KLEENE_STAR = "*";

    private final Map<ElementSelector, List<Action>> rules = new ConcurrentHashMap<>();

    public EasyMsSimpleRuleStore(Context context) {
        setContext(context);
    }

    @Override
    public void addRule(ElementSelector elementSelector, Action action) {
        action.setContext(context);
        List<Action> a4p = rules.get(elementSelector);
        if (a4p == null || action instanceof EasyMsOverride) {
            a4p = new ArrayList<>();
            rules.put(elementSelector, a4p);
        }
        a4p.add(action);
    }

    @Override
    public void addRule(ElementSelector elementSelector, String actionClassName) {
        Action action = null;
        try {
            action = (Action) OptionHelper.instantiateByClassName(actionClassName, Action.class, context);
        } catch (Exception e) {
            addError("Could not instantiate class [" + actionClassName + "]", e);
        }
        if (action != null) {
            addRule(elementSelector, action);
        }
    }

    @Override
    public List<Action> matchActions(ElementPath elementPath) {
        List<Action> actionList;

        if ((actionList = fullPathMatch(elementPath)) != null) {
            return actionList;
        } else if ((actionList = suffixMatch(elementPath)) != null) {
            return actionList;
        } else if ((actionList = prefixMatch(elementPath)) != null) {
            return actionList;
        } else if ((actionList = middleMatch(elementPath)) != null) {
            return actionList;
        } else {
            return null;
        }
    }

    private List<Action> fullPathMatch(ElementPath elementPath) {
        for (Map.Entry<ElementSelector, List<Action>> entry : rules.entrySet()) {
            if (entry.getKey().fullPathMatch(elementPath)) {
                return entry.getValue();
            }
        }
        return null;
    }

    private List<Action> suffixMatch(ElementPath elementPath) {
        int max = 0;
        ElementSelector longestMatchingElementSelector = null;

        for (ElementSelector selector : rules.keySet()) {
            if (isSuffixPattern(selector)) {
                int r = selector.getTailMatchLength(elementPath);
                if (r > max) {
                    max = r;
                    longestMatchingElementSelector = selector;
                }
            }
        }

        if (longestMatchingElementSelector != null) {
            return rules.get(longestMatchingElementSelector);
        } else {
            return null;
        }
    }

    private boolean isSuffixPattern(ElementSelector p) {
        return (p.size() > 1) && p.get(0).equals(KLEENE_STAR);
    }

    private List<Action> prefixMatch(ElementPath elementPath) {
        int max = 0;
        ElementSelector longestMatchingElementSelector = null;

        for (ElementSelector selector : rules.keySet()) {
            String last = selector.peekLast();
            if (isKleeneStar(last)) {
                int r = selector.getPrefixMatchLength(elementPath);
                if ((r == selector.size() - 1) && (r > max)) {
                    max = r;
                    longestMatchingElementSelector = selector;
                }
            }
        }

        if (longestMatchingElementSelector != null) {
            return rules.get(longestMatchingElementSelector);
        } else {
            return null;
        }
    }

    private boolean isKleeneStar(String last) {
        return KLEENE_STAR.equals(last);
    }

    private List<Action> middleMatch(ElementPath path) {

        int max = 0;
        ElementSelector longestMatchingElementSelector = null;

        for (ElementSelector selector : rules.keySet()) {
            String last = selector.peekLast();
            String first = null;
            if (selector.size() > 1) {
                first = selector.get(0);
            }
            if (isKleeneStar(last) && isKleeneStar(first)) {
                List<String> copyOfPartList = selector.getCopyOfPartList();
                if (copyOfPartList.size() > 2) {
                    copyOfPartList.remove(0);
                    copyOfPartList.remove(copyOfPartList.size() - 1);
                }

                int r = 0;
                ElementSelector clone = new ElementSelector(copyOfPartList);
                if (clone.isContainedIn(path)) {
                    r = clone.size();
                }
                if (r > max) {
                    max = r;
                    longestMatchingElementSelector = selector;
                }
            }
        }

        if (longestMatchingElementSelector != null) {
            return rules.get(longestMatchingElementSelector);
        } else {
            return null;
        }
    }

    @Override
    public String toString() {
        return "EasyMsSimpleRuleStore ( rules = " + this.rules + "   )";
    }
}